///************************************************************************
/// <copyrigth>2018-2019 Corporation.All Rights Reserved</copyrigth>
/// <author>tangyingzhong</author>
/// <contact>tangyz114987@outlook.com</contact>
/// <version>v1.0.0</version>
/// <describe>
/// Load module with special interfaces. Note: your module must have two interface 
/// They are CreateModule , DestoryModule
///</describe>
/// <date>2019/9/2</date>
///***********************************************************************
#ifndef MODULELOADER_H
#define MODULELOADER_H

#include <string>
#include "DllLoader/DllLoader.h"

namespace SystemTool
{
	template<class T>
	class ModuleLoader
	{
	public:
		typedef void None;
		typedef bool Boolean;
		typedef SystemTool::DllLoader* Loader;
		typedef T* PluginInterface;

	public:
		// Construct the ModuleLoader
		ModuleLoader(std::string strModuleFilePath) :
			m_strModuleName(""),
			m_strModuleFilePath(strModuleFilePath),
			m_pDllLoader(NULL),
			m_pPluginInterface(NULL),
			m_bDisposed(false)
		{
			Initialize();
		}

		// Detructe the ModuleLoader
		~ModuleLoader()
		{
			Destory();
		}

	private:
		// Forbid the Copy ModuleLoader
		ModuleLoader(const ModuleLoader& other) {	}

		// Forbid the assigment of ModuleLoader
		ModuleLoader& operator=(const ModuleLoader& other) { return *this; }

	public:
		// Get module interface 
		T* GetModuleInterface() const
		{
			return GetPluginInterface();
		}

		// Get the ModuleName
		inline std::string GetModuleName() const
		{
			return m_strModuleName;
		}

	private:
		// Load the module
		Boolean Load()
		{
			Loader pDllLoader = GetDllLoader();

			if (pDllLoader == NULL)
			{
				return false;
			}

			if (GetModuleFilePath().empty())
			{
				return false;
			}

			if (!IsLoad())
			{
				if (!pDllLoader->Load(GetModuleFilePath()))
				{
					return false;
				}

				if(!pDllLoader->IsHasFunc("CreateModule"))
				{
					return false;
				}

				// Craete the module
				CreateTheModule();
			}

			return true;
		}

		// Unload the module
		Boolean Unload()
		{
			if (GetDllLoader() == NULL)
			{
				return false;
			}

			if (IsLoad())
			{
				// Destory the module
				DestoryTheModule();

				GetDllLoader()->Unload();
			}

			return true;
		}

		// Is load the module
		Boolean IsLoad()
		{
			if (GetDllLoader() == NULL)
			{
				return false;
			}

			return GetDllLoader()->IsLoad();
		}

		// Craete the module
		None CreateTheModule()
		{
			Loader pDllLoader = GetDllLoader();

			if (pDllLoader == NULL)
			{
				return;
			}

			auto fCreateModule = pDllLoader->GetInnerObject().get<PluginInterface()>("CreateModule");

			PluginInterface pInterface = fCreateModule();

			SetModuleInterface(pInterface);
		}

		// Destory module
		None DestoryTheModule()
		{
			Loader pDllLoader = GetDllLoader();

			if (pDllLoader == NULL)
			{
				return;
			}

			auto fDestoryModule = pDllLoader->GetInnerObject().get<void(PluginInterface)>("DestoryModule");

			fDestoryModule(GetPluginInterface());
		}

		// Set module interface
		None SetModuleInterface(PluginInterface pInterface)
		{
			SetPluginInterface(pInterface);
		}

		// Configure module name 
		None ConfigureModuleName()
		{
			if (GetModuleFilePath().empty())
			{
				SetModuleName("");

				return;
			}

			int iPos = GetModuleFilePath().find_last_of('\\');

			if (iPos == -1)
			{
				SetModuleName("");

				return;
			}

			std::string strModuleName = GetModuleFilePath().substr(iPos + 1);

			SetModuleName(strModuleName);
		}

	private:
		// Init the module loader
		None Initialize()
		{
			// Configure the module name
			ConfigureModuleName();

			// Create a dll loader
			CreateDllLoader();

			// Load the module
			Load();
		}

		// Destory the module loader
		None Destory()
		{
			if (!GetDisposed())
			{
				SetDisposed(true);

				// Unload the module
				Unload();

				// Destory the dll loader
				DestoryDllLoader();
			}
		}

		// Create a dll loader
		None CreateDllLoader()
		{
			SetDllLoader(new DllLoader());
		}

		// Destory the dll loader
		None DestoryDllLoader()
		{
			if (GetDllLoader())
			{
				delete GetDllLoader();

				SetDllLoader(NULL);
			}
		}

	private:
		// Set the ModuleName
		inline None SetModuleName(std::string strModuleName)
		{
			m_strModuleName = strModuleName;
		}

		// Get the ModuleFilePath
		inline std::string GetModuleFilePath() const
		{
			return m_strModuleFilePath;
		}

		// Set the ModuleFilePath
		inline void SetModuleFilePath(std::string strModuleFilePath)
		{
			m_strModuleFilePath = strModuleFilePath;
		}

		// Get the DllLoader
		inline Loader GetDllLoader() const
		{
			return m_pDllLoader;
		}

		// Set the DllLoader
		inline None SetDllLoader(Loader pDllLoader)
		{
			m_pDllLoader = pDllLoader;
		}

		// Get the PluginInterface
		inline PluginInterface GetPluginInterface() const
		{
			return m_pPluginInterface;
		}

		// Set the PluginInterface
		inline None SetPluginInterface(PluginInterface pPluginInterface)
		{
			m_pPluginInterface = pPluginInterface;
		}

		// Get disposed status
		inline Boolean GetDisposed() const
		{
			return m_bDisposed;
		}

		// Set the disposed status
		inline None SetDisposed(Boolean bDisposed)
		{
			m_bDisposed = bDisposed;
		}

	private:
		// Module name
		std::string m_strModuleName;

		// Module file path
		std::string m_strModuleFilePath;

		// Dll loader
		Loader m_pDllLoader;

		// Module interface
		PluginInterface m_pPluginInterface;

		// Disposed status
		Boolean m_bDisposed;
	};
}

#endif // MODULELOADER_H
